package org.judo.frontcontroller;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Logger;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.judo.admin.identity.AppUser;
import org.judo.application.Application;
import org.judo.components.ComponentCreationException;
import org.judo.components.ComponentManager;
import org.judo.components.HTMLControl;
import org.judo.components.JudoComponent;
import org.judo.components.MustBeLoggedInException;
import org.judo.components.state.ComponentPersistence;
import org.judo.controller.DefaultAction;
import org.judo.controller.PageController;
import org.judo.controller.Params;
import org.judo.database.JudoDatabaseManager;
import org.judo.frontcontroller.exceptions.ActionException;
import org.judo.frontcontroller.exceptions.ContollerLoadingException;
import org.judo.frontcontroller.exceptions.IllegalExplicitForwardException;
import org.judo.frontcontroller.exceptions.NoContollerFoundException;
import org.judo.frontcontroller.exceptions.NoMethodException;
import org.judo.frontcontroller.exceptions.ProcessingException;
import org.judo.frontcontroller.exceptions.URLException;
import org.judo.frontcontroller.exceptions.URLMappingManager;
import org.judo.menu.Menu;
import org.judo.menu.MenuItem;
import org.judo.menu.MenuManager;
import org.judo.properties.Props;
import org.judo.propertyobject.PropertyNotFoundException;
import org.judo.propertyobject.PropertyObject;
import org.judo.propertyobject.ValidationError;
import org.judo.script.ScriptInterpreter;
import org.judo.security.SecurityManager;
import org.judo.service.ServiceManager;
import org.judo.service.systemservices.LoggingService;
import org.judo.util.JavaNameConversion;
import org.judo.util.Util;
import org.judo.validation.FieldValidator;
import org.judo.validation.ValidationMessage;
import org.judo.validation.ValidationSystemException;

import sun.text.NormalizerUtilities;


public class JudoFrontController extends HttpServlet
{
	private static String basePackage;
	private static String errorPage;
	private static String messagePage;
	private static String detailedError;
	private static String applicationClass;
	private static String simpleURLBase;
	private static String prettyURLBase;
	private static String defaultForward;
	
	private static boolean javacTested = false;
	private static boolean javacOK = false;
	private static boolean devServerMode = false;

	public void doGet(HttpServletRequest request,
	        HttpServletResponse response)
	  throws IOException, ServletException 
	{
		forward(request,response);
	}
	
	public void doPost(HttpServletRequest request,
	        HttpServletResponse response)
	  throws IOException, ServletException 
	{
		forward(request,response);
	}
	
	protected void forward(HttpServletRequest request,
	        HttpServletResponse response)
	  throws IOException, ServletException
	{	
		String forwardURL = null;
		
		try
		{
			String servletPath = request.getServletPath();
			Util.out("servletPath "+servletPath);
			String rootContext = getRootContext(request);
			AppUser user = (AppUser)request.getSession().getAttribute("__USER");
			
			setImplicitContextVars(request,rootContext,servletPath);
			ComponentManager.populateSession(request.getSession());
			
			if (SecurityManager.isProtectedURL(servletPath) && user == null)
				forwardURL = loginForProtected(servletPath,request);
			else if (SecurityManager.isProtectedURL(servletPath) && !SecurityManager.passesSecurityCheck(user,servletPath))
				forwardURL = "/admn/not_allowed.jspx";
			else if (servletPath.equals("/home"))
				forwardURL = defaultForward; 
			else if (request.getRequestURI().contains("/"+simpleURLBase+"/"))
				forwardURL = SimpleURLManager.parseSimpleURL(request);
			else if (request.getRequestURI().contains("/"+prettyURLBase+"/"))
				forwardURL = PrettyURLManager.parsePrettyURL(request,prettyURLBase);
			else if (URLMappingManager.isAMappedPath(request))
				forwardURL = URLMappingManager.parseURL(request);
			else
				forwardURL = processRegularURL(servletPath,request,response);
		}
		catch(Exception e)
		{
			handleGeneralError(e,request);
			forwardURL = "/system/failed_request.jsp";
		}
		
		String dump = request.getParameter("__dump");
		
		if (dump != null && dump.equals("true"))
		{
			setFailVariables("","",request);
			forwardURL = "/system/dump.jsp";
		}

		RequestDispatcher dispatcher = 
			request.getRequestDispatcher(forwardURL);

		if (dispatcher == null)
		{
			setFailVariables("Judo can not determine where to forward this request to after processing. Unfortunatly no other information is available on the cause of this error. This is possibly an internal Judo Framework bug. The forward value was: "+forwardURL,forwardURL,request);
			forwardURL = "/system/failed_request.jsp";
			dispatcher = 
				request.getRequestDispatcher(forwardURL);
		}
		
		dispatcher.forward(request,response);
	}

	private String loginForProtected(String servletPath,HttpServletRequest request)
	{
		request.getSession().setAttribute("__page_attempt",servletPath);
		return "/users/user_controller/login.do";	
	}

	public static void setAsDevServerMode()
	{
		devServerMode = true;
	}
	
	public void init(ServletConfig config) throws ServletException 
	{
		Props.load();
		MenuManager.loadDefinedMenus();
		
		basePackage = Props.get("judo.controllers.base.classpath");
		errorPage = Props.get("judo.error.page");
		messagePage = Props.get("judo.msg.page");
		detailedError = Props.get("judo.detailed.error.message.flag");
		applicationClass = Props.get("judo.application.class");
		simpleURLBase = Props.get("judo.simple.url.base");
		prettyURLBase = Props.get("judo.pretty.url.base");
		defaultForward = Props.get("judo.home.page.forward");
		
		defineApplicationClass();
		defineDefaultServices();
		loadMappings();
	}
	
	private void loadMappings()
	{
		SimpleURLManager.load();
		URLMappingManager.load();
		PrettyURLManager.load();
	}

	private void defineApplicationClass()
	{
		try
		{
			Application app = 
				(Application)Class.forName(applicationClass).newInstance();
			
			ComponentManager.setApplication(app);
			
			app.configureApplication();
		}
		catch(Exception e)
		{
			e.printStackTrace();
			System.out.println("Fatal Error: "+e);
		}
	}

	private void defineDefaultServices()
	{
		try
		{
			LoggingService logger = new LoggingService();
			logger.init();
			
			ServiceManager.defineService("loggingService",logger);
			
			JudoDatabaseManager.load();
			
			loadSecurity();
		}
		catch(Throwable e)
		{
			System.out.println("SERVICE ERROR: "+e);
			//e.printStackTrace();
		}
	}
	
	private void loadSecurity()
		throws Exception
	{
		SecurityManager.init();
	}
	
	private void setImplicitContextVars(HttpServletRequest request,String rootContext,String servletPath)
	{
		AppUser user = (AppUser)request.getSession().getAttribute("__USER");	
		String menus[] = MenuManager.menus();
		
		for (String menuName:menus)
		{
			String menuHTML = "<ul id=\"navlist\">\n";
			
			Menu menu = MenuManager.menu(menuName);
			MenuItem menuItems[] = SecurityManager.cullMenuItems(user,menu);
			
			for (MenuItem item:menuItems)
			{
				menuHTML += "<li><a href=\""+rootContext+"/"+item.link+"\">"+item.display+"</a>\n";
			}
			
			menuHTML += "</ul>\n";
			
			request.setAttribute(menuName,menuHTML);
		}
		
		request.setAttribute("rootContext",rootContext);
		request.setAttribute("detailedError",detailedError);
		request.setAttribute("validateForm","<input type=\"hidden\" name=\"judo_validation_source\" value=\""+servletPath+"\"><input type=\"hidden\" name=\"__validate\" value=\"true\">");
		
		Iterator<String> names= Props.propNames();
		
		//todo: cached?
		while (names.hasNext())
		{
			String name = names.next();
			request.setAttribute(name.replace(".","_"),Props.get(name));
		}
	}

	private String processRegularURL(String servletPath, HttpServletRequest request, HttpServletResponse response) 
		throws ActionException, PropertyNotFoundException, ContollerLoadingException
	{		
		String forwardURL = null;
		
		if (servletPath.contains(".jspx"))
		{
			forwardURL = servletPath.replace(".jspx",".jsp");
			setVariables(request);
		}
		else
		{
			String basePath = servletPath.substring(1,servletPath.length()-3);
			String parts[] = basePath.trim().split("/");
			
			RequestInfo info = getRequestInfo(servletPath);
			
			try{
				forwardURL = processController(info,request,response,servletPath);
			}
			catch (ValidationFailed e)
			{
				String newForward = request.getParameter("judo_validation_source");
					
				if (newForward == null || newForward.equals(""))
				{
					handleErrorMessage("To handle validation error messages you need to add '${validateForm}' inside of your form",request);
					request.setAttribute("ignore_valdiation","true");
					return errorPage;
				}
				else
				{
					String error_msgs = "<div id=\"error_msgs\"><ul>\n";
					List<ValidationMessage> msgs = e.getMsgs();
					
					for (ValidationMessage msg:msgs)
					{
						if (msg.field == null)
							error_msgs += "<li>"+msg.message+"\n";
						else
							error_msgs += "<li>Problem with: "+msg.field+" - "+msg.message+"\n";
						
						request.setAttribute(msg.field+"_error","<div id=\"error_msg\">"+msg.message+"</div>");
					}
					
					error_msgs += "</ul></div>\n";
					
					request.setAttribute("validation_errors", error_msgs);
				}
				
				request.setAttribute("ignore_valdiation","true");
				forwardURL = newForward;
			}
			catch(NoContollerFoundException e)
			{	
				handleNoContollerException(e,request);
				forwardURL = "/system/no_controller.jsp";
			}
			catch(IllegalExplicitForwardException e)
			{
				handleIllegalForward(request);
				forwardURL = errorPage;
			}
			catch(NoMethodException e)
			{
				handleErrorMessage("By default, Judo assumes that request URLs will come in with the format:<br><br> http://[host]/[context]/...[optional_path].../[controller_name]/[action_name].do.<br><br>  For this request there is a action name '"+e.getActionName()+"' but there is not a corrisponding method...<br><br> '"+e.getMethodName()+"(HttpServletRequest,HttpServletResponse)' <br><br>...in the class '"+e.getClassName()+"'",request);
				forwardURL = errorPage;
			}
			catch(ProcessingException e)
			{
				handleProcessingException(e,request);
				forwardURL = errorPage;
			} 
		}
		
		return forwardURL;
	}
	
	private String processController(RequestInfo info,HttpServletRequest request, HttpServletResponse response,String servletPath) 
		throws IllegalExplicitForwardException,ActionException,NoContollerFoundException,PropertyNotFoundException, 
			NoMethodException, ProcessingException, ContollerLoadingException, ValidationFailed
	{
		String forwardURL = null;
		PageController page = null;
		
		try
		{
			String methodName = JavaNameConversion.getMemberName(info.actionName);
			String className = JavaNameConversion.getClassName(info.controllerName);
			String fullJavaName = info.pathName+"."+className;
			
			String curPath = basePackage;
			
			if (info.pathName != null && !info.pathName.equals(""))
				curPath += "."+info.pathName;
			
			if (!basePackage.equals("") && !basePackage.equals("none"))
				className = curPath+"."+className;
			
			StringBuffer defaultForward = new StringBuffer();
			defaultForward.append("");
				
			try
			{
				page = executePageController(info,className,methodName,request,response,defaultForward);
			}
			catch(MustBeLoggedInException e)
			{
				request.getSession().setAttribute("__page_attempt",servletPath);
				return "/admin/user_controller/login.do";
			}
			catch(NoContollerFoundException e)
			{
				try
				{
					ScriptInterpreter script = new ScriptInterpreter();
					page = script.execute(info.pathName,info.controllerName,info.actionName,request);
				}
				catch(ClassNotFoundException e2)
				{
					throw e;
				}
			}
			
			/*
			 * Take the properties from the page controller
			 * and set them as attributes in the request
			 * object 
			 */
			page.writePropsToRequest();
			
			/*
			 * See if the page had a forward defined. 
			 * 
			 * Next see if the forward is defined in
			 * the '_forward' URL parameter and test
			 * security rule
			 * 
			 * If neither, then use the convention to 
			 * define the URL
			 */
			
			forwardURL = page.showPage();
			
			if (forwardURL.equals(PageController.NO_FORWARD_URL))
			{
				String urlForwardParam = request.getParameter("_forward");
				
				if (urlForwardParam != null && !urlForwardParam.equals(""))
				{
					if (urlForwardParam.contains(".jspx") || urlForwardParam.contains(".do"))
						forwardURL = urlForwardParam;
					else
						throw new IllegalExplicitForwardException();
				}
				else
				{
					if (defaultForward.toString().equals(""))
						forwardURL = info.jsp;
					else
						forwardURL = info.pathName.replace(".","/")+"/"+defaultForward.toString(); 
				}
			}
			
			/*
			 * Check to see if there are any session values
			 * that need to be grabbed out
			 * TODO:  Do the session values
			 */
			
			page.showPage(PageController.NO_FORWARD_URL);
		}
		catch (NoContollerFoundException e1)
		{	
			throw e1;
		}
		catch(NoMethodException e)
		{
			if (page != null)
				page.rollbackAllTransactions();
			
			throw e;
		}
		catch(ValidationFailed e)
		{
			if (page != null)
				page.rollbackAllTransactions();
			
			throw e;
		}
		catch(IllegalExplicitForwardException e)
		{
			if (page != null)
				page.rollbackAllTransactions();
			
			throw e;
		}
		catch(ActionException e)
		{
			if (page != null)
				page.rollbackAllTransactions();
			
			throw e;
		}
		catch(ProcessingException e)
		{
			if (page != null)
				page.rollbackAllTransactions();
			
			throw e;
		}
		catch(Exception e)
		{
			if (page != null)
				page.rollbackAllTransactions();
			
			e.printStackTrace();
			throw new ContollerLoadingException("Error with controller '"+info.controllerName+"' and action '"+info.actionName+"': "+e+"\n");
		}
		finally
		{
			if (page != null)
				page.completeProcessing();
		}
		
		return forwardURL;
	}
		
	private void setVariables(HttpServletRequest request)
	{
		Enumeration names = request.getParameterNames();
		
		while (names.hasMoreElements())
		{
			String name = (String)names.nextElement();
			String values[] = request.getParameterValues(name);
			
			if (values.length > 1)
			{
				request.setAttribute(name,values);
			}
			else
			{
				String value = values[0];
				request.setAttribute(name,value);				
			}
		}
	}

	protected PageController executePageController(
			RequestInfo info, String fullJavaName,String methodName,
			HttpServletRequest req, HttpServletResponse resp,StringBuffer defaultForward)
				throws NoContollerFoundException, 
						ActionException, 
						NoMethodException, 
						ProcessingException, 
						ContollerLoadingException, 
						ValidationFailed, MustBeLoggedInException
	{
		PageController controller = null;
		
		try
		{	
			String className = JavaNameConversion.getClassName(info.controllerName);
			
			Object obj = null;
			
			try{
				try{
					obj = ComponentManager.loadComponent(req,fullJavaName,null);
				}
				catch(ClassNotFoundException cnf1)
				{
					String newFullJavaName = "org.judo.admin."+className;
					obj = ComponentManager.loadComponent(req,newFullJavaName,null);
				}
			}
			catch(ClassNotFoundException e)
			{
				NoContollerFoundException ncException = 
					new NoContollerFoundException();
				
				ncException.setAction(info.actionName);
				ncException.setClassName(fullJavaName);
				ncException.setController(info.controllerName);
				ncException.setMethodname(methodName);
				
				throw ncException;
			}
			
			if (!(obj instanceof PageController))
			{
				throw new ProcessingException("Attempted to load '' but it is not a subclass of 'PageController'");
			}
			
			controller = (PageController)obj;
			controller.populateRequest(req);
			controller.populateResponce(resp);
			controller.copyProps(req);
			List<ValidationMessage> errorMsgs = controller.messages(); 
			
			Method actionMethod = 
					getActionMethod(obj,methodName,fullJavaName,info,defaultForward,req);
			
			List<ValidationMessage> msgs = performValidation(obj,req,actionMethod);
			msgs.addAll(errorMsgs);
			
			if (msgs.size() > 0)
				throw new ValidationFailed(msgs);
			
			callPageLifeCycleMethods(
					obj,methodName,actionMethod,fullJavaName,info,defaultForward,req);
		}
		catch(MustBeLoggedInException e)
		{
			throw e;
		}
		catch(NoMethodException e)
		{
			throw e;
		}
		catch(NoContollerFoundException e)
		{
			throw e;
		}
		catch(ProcessingException e)
		{
			throw e;
		}
		catch(ValidationFailed e)
		{
			throw e;
		}
		catch(Exception e)
		{
			//todo: Rollback?
			if (e instanceof ValidationError)
			{
				ValidationError ve = (ValidationError)e;
				List<ValidationMessage> msgs = new ArrayList<ValidationMessage>();
				msgs.add(new ValidationMessage(ve.getName(),ve.getMessage()));
				throw new ValidationFailed(msgs);
			}
			
			e.printStackTrace();
			throw new ContollerLoadingException("Error processing PageController for controller '"+info.controllerName+"' and action '"+info.actionName+"': "+e+"\n");
		}
		
		
		return controller;
	}
	
	private List<ValidationMessage> performValidation(Object obj, HttpServletRequest req,Method method) 
		throws ValidationSystemException, ValidationFailed
	{
		List<ValidationMessage> msgs = null;
		
		FieldValidator validator = 
			new FieldValidator((PropertyObject)obj,req,method.getName());
		
		if (validator.validationFailed())
			msgs = validator.getValidationMessages();
		else
			msgs = new ArrayList<ValidationMessage>(); 
		
		return msgs;
	}
	
	protected Method getActionMethod(Object obj, String methodName, 
			String fullJavaName, RequestInfo info,StringBuffer defaultForward, HttpServletRequest req) 
				throws NoMethodException
	{
		try
		{
			Method method = null;
			
			if (methodName.equals("default"))
				method = getDefaultMethod(obj);
			else if (methodName.equals("action"))
				method = findButtonAction(methodName,obj,req);
				
			if (method == null)
			{
				method = obj.getClass().getDeclaredMethod(methodName,
						new Class[]{});
			}
			else
			{
				String pageName = JavaNameConversion.getUnderscoreName((method.getName()));
				defaultForward.append(pageName+".jsp");
			}
			
			return method;
		}
		catch (NoSuchMethodException e)
		{
			NoMethodException ex = new NoMethodException();
			ex.setMethodName(methodName);
			ex.setClassName(fullJavaName);
			ex.setActionName(info.actionName);
			throw ex;
		}
	}

	protected void callPageLifeCycleMethods(Object obj, String methodName, Method method,
			String fullJavaName, RequestInfo info,StringBuffer defaultForward, HttpServletRequest req) 
		throws NoMethodException, ProcessingException, ValidationError 
	{
		PageController page = (PageController)obj;
		
		try
		{
			page.beforeRequestAction();	
			callActionMethod(obj,methodName,method,fullJavaName,info,defaultForward,req);
			page.afterRequestAction();
		}
		catch(ValidationError e)
		{
			page.onError(e);
			throw e;
		}
		catch(NoMethodException e)
		{
			page.onError(e);
			throw e;
		}
		catch(ProcessingException e)
		{
			page.onError(e);
			throw e;
		}
	}
	
	protected void callActionMethod(Object obj, String methodName,Method method, 
			String fullJavaName, RequestInfo info,StringBuffer defaultForward, HttpServletRequest req) 
		throws NoMethodException, ProcessingException, ValidationError
	{
		try
		{
			/*Params params = method.getAnnotation(Params.class);
			
			if (params != null)
			{
				Class types[] = method.getParameterTypes();
				String paramNames[] = params.value().split(",");
				Object args[] = new Object[paramNames.length];
				
				int i = 0;
				for (String param:paramNames)
				{
					types[i].
					i++;
				}
			}
			else*/
				method.invoke(obj,new Object[]{});
		}
		catch(InvocationTargetException e)
		{
			Throwable t = e.getCause();
			
			if (t instanceof ValidationError)
				throw (ValidationError)t;
		
			if (t instanceof RollBackTransactions)
				((JudoComponent)obj).rollbackAllTransactions();
			
			ProcessingException p = new ProcessingException();
			p.setInnerStackTrace("Error executing action '"+fullJavaName+"."+methodName+"()':\n\n "+t+"\n\n"+getStackTracteString(t));
			throw p;
		}
		catch(Exception e)
		{	
			throw new ProcessingException("Error executing action '"+fullJavaName+"."+methodName+"()': "+e+"\n");
		}
	}
	
	private Method findButtonAction(String methodName, Object obj, HttpServletRequest req) 
		throws SecurityException
	{
		Enumeration list = req.getParameterNames();
		
		while (list.hasMoreElements())
		{
			String name = (String)list.nextElement();
			
			if (name.startsWith("btn_"))
			{
				String newMethodName = JavaNameConversion.getMemberName(name); 
					
				try
				{
					return obj.getClass().getDeclaredMethod(newMethodName,
							new Class[]{});
				} 
				catch (NoSuchMethodException e){
				}
			}
		}
		
		return null;
	}

	private Method getDefaultMethod(Object obj)
	{
		Method methods[] = obj.getClass().getDeclaredMethods();
		
		for (Method method:methods)
		{
			Annotation def = method.getAnnotation(DefaultAction.class);
			
			if (def != null)
				return method;
		}
		
		return null;
	}

	protected RequestInfo getRequestInfo(String servletPath)
	{
		RequestInfo info = new RequestInfo();
		
		String basePath = servletPath.substring(1,servletPath.length()-3);
		String parts[] = basePath.trim().split("/");
		
		int times = 1;
		boolean first = true;
		boolean firstJsp = true;
		
		info.actionName = null;
		info.controllerName = null;
		info.pathName = "";
		
		for (int i=parts.length-1;i!=-1;i--)
		{	
			if (times == 1)
				info.actionName = parts[i];
			else if (times == 2)
				info.controllerName = parts[i];
			else
			{
				if (first)
					first = false;
				else
					info.pathName = "."+info.pathName;
				
				info.pathName = parts[i]+info.pathName;
			}
			
			times++;
		}
		
		info.jsp = "/"+info.pathName.replace(".","/")+"/"+info.actionName+".jsp";
		info.jsp = info.jsp.replace("//","/");
		
		return info;
	}
	
	private void handleNoContollerException(NoContollerFoundException e,HttpServletRequest request)
	{
		String message = e.getDetails();
		String badURL = request.getServletPath();
		request.setAttribute("ncEx",e);
		request.setAttribute("base_package",basePackage);
		setFailVariables(message,badURL,request);
		
		String urlForwardParam = request.getParameter("_forward");
		if (urlForwardParam != null && !urlForwardParam.equals(""))
			request.setAttribute("urlForwardParam",urlForwardParam);
		else
			request.setAttribute("urlForwardParam","none");
	}
	
	private void handleIllegalForward(HttpServletRequest request)
	{
		String message = "Only URLS with extensions '.jspx' and '.do' can be used as explicit URLs defined in the '_forward' HTTP request parameter.";
		String badURL = request.getServletPath();
		setFailVariables(message,badURL,request);
	}
	
	private void handleErrorMessage(String message,HttpServletRequest request)
	{
		String badURL = request.getServletPath();
		setFailVariables(message,badURL,request);
	}
	
	private String getStackTracteString(Throwable e)
	{
		StringWriter writer = new StringWriter();
		PrintWriter pwriter = new PrintWriter(writer);
		e.printStackTrace(pwriter);
		return writer.toString();	 
	}

	private void handleProcessingException(ProcessingException e,HttpServletRequest request)
	{
		String trace = e.getInnerStackTrace().replace("\n","\n<br>");
		String badURL = request.getServletPath();
		setFailVariables(trace,badURL,request);
	}
	
	private void handleGeneralError(Exception e,HttpServletRequest request)
	{
		String err = e.getMessage();
		
		if (err == null)
			err = "";
		
		String message = err.replace("\n", "\n\n<br><br>");
		message += "\n\n<br><br>"+getStackTracteString(e).replace("\n", "<br>\n");
		String badURL = request.getServletPath();
		setFailVariables(message,badURL,request);
	}
	
	private void setFailVariables(String message, String badURL, HttpServletRequest request)
	{
		request.setAttribute("bad_url", badURL);
		request.setAttribute("problem_text", message);
		
		String params = "<ul>\n";
		String cookieStr = "<ul>\n";
		String headers = "<ul>\n";
		String sessions = "<ul>\n";
		
		Enumeration names = request.getParameterNames();
		
		while (names.hasMoreElements())
		{
			String name = (String)names.nextElement();
			
			String values[] = request.getParameterValues(name);
			
			if (values.length > 1)
			{
				request.setAttribute(name,values);
			}
			else
			{
				String value = values[0];
				params += "<li><b>"+name+"</b>= '"+value+"'</li>\n";				
			}
		}
		
		params += "</ul>\n";
		request.setAttribute("problem_params",params);
		
		Cookie cookies[] = request.getCookies();
		
		if (cookies != null)
		{
			for (int i=0;i<cookies.length;i++)
			{
				Cookie cookie = cookies[i];
				cookieStr += "<li><b>"+cookie.getName()+"</b>= '"+cookie.getValue()+"' (Max Age: "+cookie.getMaxAge()+")</li>\n";
			}
		}
		cookieStr += "</ul>\n";
		request.setAttribute("cookies",cookieStr);
		
		Enumeration headerNames =  request.getHeaderNames();
		
		while (headerNames.hasMoreElements())
		{
			String name = (String)headerNames.nextElement();
			String value = request.getHeader(name);
			headers += "<li><b>"+name+"</b>= '"+value+"'</li>\n";
		}
		
		headers += "</ul>\n";
		request.setAttribute("headers",headers);
		
		Enumeration attrNames =  request.getSession().getAttributeNames();
		
		while (attrNames.hasMoreElements())
		{
			String name = (String)attrNames.nextElement();
			Object value = request.getSession().getAttribute(name);
			sessions += "<li><b>"+name+"</b>= '"+value+"'</li>\n";
		}
		
		sessions += "</ul>\n";
		request.setAttribute("sessions",sessions);
		
		ComponentPersistence cp = 
			ComponentPersistence.getInstance(request.getSession());
		
		String statefulComponentsStr = "<ul>\n";
		
		Iterator statefulComponents = cp.getStatefulNames();
		
		while (statefulComponents.hasNext())
		{
			String componentName = (String)statefulComponents.next();
			JudoComponent component = (JudoComponent)cp.getComponentObject(componentName);
			
			statefulComponentsStr += "<li>Component: '"+componentName+"\n";
			statefulComponentsStr += "<ul>\n";
			
			Iterator props = component.keySet().iterator();
			
			while (props.hasNext())
			{
				String prop = (String)props.next();
				
				try
				{
					Object rawValue = component.getRawPropertyValue(prop);
					statefulComponentsStr += "<li>Property: '"+prop+"' = "+rawValue+"\n";
				} 
				catch (PropertyNotFoundException e)
				{
					e.printStackTrace();
				}
			}
			
			statefulComponentsStr += "</ul>\n";
		}
		
		statefulComponentsStr += "</ul>\n";
		request.setAttribute("stateful",statefulComponentsStr);
		
		String statefulGroupComponentsStr = "<ul>\n";
		
		Iterator groupNames = cp.getAllGroupNames();
		
		while (groupNames.hasNext())
		{
			String groupName = (String)groupNames.next();
			statefulGroupComponentsStr += "<li>Group Name: '"+groupName+"'\n";
			statefulGroupComponentsStr += "<ul>\n";
			
			HashMap groupMap = cp.getGroupComponents(groupName);
			Iterator groupComponents = groupMap.keySet().iterator();
			
			while (groupComponents.hasNext())
			{
				String groupComponentName = (String)groupComponents.next();
				statefulGroupComponentsStr += "<li>Component Name: '"+groupComponentName+"'\n";
				statefulGroupComponentsStr += "<ul>\n";
				
				JudoComponent component = (JudoComponent)groupMap.get(groupComponentName);
				
				Iterator props = component.keySet().iterator();
				
				while (props.hasNext())
				{
					String prop = (String)props.next();
					
					try
					{
						Object rawValue = component.getRawPropertyValue(prop);
						statefulGroupComponentsStr += "<li>Property: '"+prop+"' = "+rawValue+"\n";
					} 
					catch (PropertyNotFoundException e)
					{
						e.printStackTrace();
					}
				}
				
				statefulGroupComponentsStr += "</ul>\n";
			}
			
			statefulGroupComponentsStr += "</ul>\n";
		}
		
		statefulGroupComponentsStr += "</ul>\n";
		request.setAttribute("statefulGroup",statefulGroupComponentsStr);
	}
	
	private String getRootContext(HttpServletRequest request)
		throws URLException
	{
		String root = null;
		
		if (request.getProtocol().toUpperCase().contains("HTTPS"))
			root = "https://";
		else if (request.getProtocol().toUpperCase().contains("HTTP"))
			root = "http://";
		else
			throw new URLException("Unexpected protocol when building absolute path: "+request.getProtocol());
		
		root += request.getHeader("Host");
		
		/*if (request.getLocalPort() != 80)
			root += ":"+request.getLocalPort();*/
		
		String context = request.getContextPath();
		
		if (context.lastIndexOf("/") > 0)
			context += "/";
		
		root += context;
		
		return root;
	}
}
